package com.general.framework.web.aop;

import com.general.framework.core.exception.*;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import com.general.framework.core.lang.Jsons;
import com.general.framework.core.lang.Strings;
import com.general.framework.core.schema.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;

import javax.servlet.http.HttpServletRequest;

/**
 * 全局异常处理
 * @author leejean
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 全局异常捕捉处理
     */
    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<Response<Void>> exceptionHandler(HttpServletRequest request, Exception e) {
        String params = null;
        try {
            params = Jsons.toJsonString(request.getParameterMap());
        } catch (Exception e2) {
            log.error("转换请求参数出错", e2);
        }
        String message = e.getMessage();
        if (Strings.isNotBlank(message) && message.contains("ClientException")){
            log.error("系统异常:{} 参数={}, message:{}", request.getRequestURI(), params, message);
        } else {
            log.error("系统异常:{} 参数={}", request.getRequestURI(), params, e);
        }
        if (Strings.isEmpty(message)) {
            message = Strings.format("{}===>{}", e.getClass().getName(), e.getStackTrace()[0].toString());
        }
        return ResponseEntity.ok(Response.error(CodeEnum.SYSTEM_ERROR, message));
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<Response<Void>> businessExceptionHandler(HttpServletRequest request, BusinessException e) {
        log.error("业务异常--> {},{}", request.getRequestURI(), e.getMessage());
        return ResponseEntity.ok(Response.error(e.getCode(), e.getMessage()));
    }

    @ExceptionHandler(ViolationException.class)
    public ResponseEntity<Response<Void>> violationExceptionHandler(HttpServletRequest request, BaseException e) {
        log.info("参数校验异常--> {},{}", request.getRequestURI(), e.getMessage());
        return ResponseEntity.ok(Response.error(e.getCode(), e.getMessage()));
    }

    /**
     * 请求参数异常
     */
    @ExceptionHandler(ServletRequestBindingException.class)
    public ResponseEntity<Response<Void>> servletRequestBindingExceptionExceptionHandler(HttpServletRequest request, Exception e) {
        log.error("请求参数异常--> {},{}", request.getRequestURI(), e.getMessage());
        return ResponseEntity.ok(Response.error(CodeEnum.VIOLATION_ERROR, e.getMessage()));
    }

    /**
     * 检验异常
     */
    @ExceptionHandler(BindException.class)
    public ResponseEntity<Response<Void>> bindExceptionHandler(HttpServletRequest request, BindException e) {
        for (ObjectError error : e.getBindingResult().getAllErrors()) {
            log.error("参数异常--> {},{}", request.getRequestURI(), error.getDefaultMessage());
            return ResponseEntity.ok(Response.error(CodeEnum.VIOLATION_ERROR.getCode(), "参数异常"));
        }
        log.error("参数异常--> {},{}", request.getRequestURI(), e.getMessage());
        return ResponseEntity.ok(Response.error(CodeEnum.VIOLATION_ERROR.getCode(), "参数异常"));
    }

    /**
     * 参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Response<Void>> methodArgumentNotValidExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        if (fieldError != null) {
            final String detailMessage = Strings.format("{}({})", fieldError.getDefaultMessage(),  fieldError.getField());
            log.error("参数检验异常--> {},{}", request.getRequestURI(), detailMessage );
            return ResponseEntity.ok(Response.error(CodeEnum.VIOLATION_ERROR.getCode(), fieldError.getDefaultMessage()));
        }
        return ResponseEntity.ok(Response.success());
    }

    /**
     * 参数校验异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<Response<Void>> httpMessageNotReadableExceptionHandler(HttpServletRequest request, HttpMessageNotReadableException e) {
        log.error("参数异常，requestBody为空--> {},{}", request.getRequestURI(), e.getMessage());
        return ResponseEntity.ok(Response.error(CodeEnum.VIOLATION_ERROR.getCode(), "参数异常"));
    }

    /**
     * 上传文件异常
     */
    @ExceptionHandler(value = MultipartException.class)
    public ResponseEntity<Response<Void>> multipartExceptionHandler(HttpServletRequest request, MultipartException e) {
        log.error("上传文件异常--> {},{}", request.getRequestURI(), e.getMessage());
        return ResponseEntity.ok(Response.error(CodeEnum.FILE_PROCESSING_ERROR, "文件操作异常"));
    }

    /**
     * 请求方式错误处理
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<Response<Void>> httpRequestMethodNotSupportedExceptionHandler(HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
        log.warn("错误的请求方式--> {},{}", request.getRequestURI(), e.getMessage());
        return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(Response.error("不支持的请求方式"));
    }

    @ExceptionHandler(value = {FeignResponseException.class})
    public ResponseEntity<Response<Void>> feignResponseExceptionHandler(HttpServletRequest request, FeignResponseException e) {
        log.error("{} feign接口调用异常,{code: {}, message:{}}", request.getRequestURI(), e.getCode(), e.getMessage(), e);
        return ResponseEntity.ok(Response.error(CodeEnum.RPC_ERROR.getCode(), e.getMessage()));
    }

    @ExceptionHandler(value = {HystrixRuntimeException.class})
    public ResponseEntity<Response<Void>> hystrixExceptionHandler(HttpServletRequest request, HystrixRuntimeException e) {
        log.error("{} 服务熔断, message:{}", request.getRequestURI(), e.getMessage());
        return ResponseEntity.ok(Response.error(CodeEnum.RPC_CIRCUIT_BREAK.getCode(), e.getMessage()));
    }


}
